package com.demo.webapp.ui;

import java.io.IOException;
import java.io.InputStream;

import javax.servlet.ServletContext;

import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.binder.DigesterLoader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.demo.common.ClassLoaderUtils;
import com.demo.webapp.ui.config.ComponentRulesModule;
import com.demo.webapp.ui.exception.LoadableResourceException;

/**
 * 组件持久对象生成类
 * 提供抽象的生成方式
 * 
 * @author ryuu.kk
 *
 */
public abstract class RepositoryBuilderBase implements RepositoryBuilder {
	
	/** log日志对象 **/
	protected final transient Log log = LogFactory.getLog(super.getClass());
	
	/**
	 * 组件配置信息配置文件路径
	 * 优先级低于configs配置
	 */
	private String config;
	
	/**
	 * 组件配置信息配置文件路径
	 * 优先级高于config
	 * 当configs存在,不对config指定的配置进行加载
	 */
	private String[] configs;
	
	/**
	 * 组件配置对象
	 * 将配置文件,DB装载到Bean对象中
	 */
	private Object target;

	/**
	 * 组件装载的策略文件
	 */
	private String[] baseNames;
	
	/**
	 * 类加载器
	 */
	private ClassLoader classLoader;
	
	/**
	 * Servlet上下文
	 */
	private ServletContext servletContext;

	/**
	 * 组件装载前
	 * 对组件加载规则进行前置处理
	 * @param module 组件加载规则配置对象
	 */
	public abstract void beforeLoad(ComponentRulesModule module);
	
	/**
	 * 组件装载后
	 * 使用Servlet上下文对加载后的组件进行处理
	 * @param servletContext ServletContext
	 */
	public abstract void afterLoad(ServletContext servletContext);
	
	// Interface implements here  -------------------------------------------------------------
	public void load() throws LoadableResourceException {
		//配置文件数据流
		InputStream input = null;
		ComponentRulesModule module = new ComponentRulesModule();
		//设置Load的前置操作
		beforeLoad(module);
		
		if (target == null) {
			throw new LoadableResourceException("Error parsing resource file: "
					+ getConfig() + " nested exception is: target class is empty.");
		}
		//使用默认地址
		if (baseNames == null || baseNames.length == 0) {
			//baseNames = new String[]{"classpath:com/demo/webapp/ui/menu/digester-menu.xml"};
			throw new LoadableResourceException("Error parsing resource file: "
					+ getConfig() + " nested exception is: baseNames is empty.");
		}

		module.setBaseNames(baseNames);
		DigesterLoader loader = DigesterLoader.newLoader(module);
		loader.setClassLoader(this.classLoader != null ? this.classLoader : Thread.currentThread().getContextClassLoader());
		Digester digester = loader.newDigester();
		
		if (configs == null || configs.length == 0) {
			configs = new String[] {this.config};
		} 
		
		for (String conf : configs) {
			try {
				// 处理数组
				// 使用Servlet加载资源
				input = getServletContext().getResourceAsStream(conf);

				// 非WEB上下文资源 
				if (input == null) {
					// 使用类加载器加载资源
					input = ClassLoaderUtils.getResourceAsStream(conf, this.classLoader);
				}
				
				if (input != null) {
					// 设置栈顶对象
					digester.push(target);
					// FileSystemResourceLoader resourceLoader = new
					// FileSystemResourceLoader();
					// Resource resource = resourceLoader.getResource(getConfig());
					// digester.parse(resource.getInputStream());
					digester.parse(input);
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new LoadableResourceException(
						"Error parsing resource file: " + getConfig()
								+ " nested exception is: " + e.getMessage());
			} finally {
				if (input != null) {
					try {
						input.close();
					} catch (IOException e) {
					}
				}
			}
		}

		afterLoad(servletContext);
	};
	
	@Override
	public void reload() throws LoadableResourceException {
		load();		
	}

	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}
	
	@Override
	public void push(Object targetObj) {
		this.target = targetObj;
	}
	@Override
	public Object pop() {
		return this.target;
	}
	
	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	// Settings here  --------------------------------------------------------------------------
	public String getConfig() {
		return config;
	}

	public void setConfig(String config) {
		this.config = config;
	}

	public String[] getConfigs() {
		return configs;
	}

	public void setConfigs(String[] configs) {
		this.configs = configs;
	}

	public void setBaseNames(String[] baseNames) {
		this.baseNames = baseNames;
	}

	public String[] getBaseNames() {
		return baseNames;
	}

	public ServletContext getServletContext() {
		return servletContext;
	}
}